<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>PF: Traffic Redirection (Port Forwarding)</title>
<link rev="made" href="mailto:www@openbsd.org">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="resource-type" content="document">
<meta name="description"   content="the OpenBSD FAQ page">
<meta name="keywords"      content="openbsd,faq,pf">
<meta name="distribution"  content="global">
</head>

<!--
Copyright (c) 2003, Nick Holland <nick@openbsd.org>
Copyright (c) 2003-2005, Joel Knight <enabled@myrealbox.com>

Permission to use, copy, modify, and distribute this documentation for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.

THE DOCUMENTATION IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS DOCUMENTATION INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS DOCUMENTATION
-->

<body bgcolor="#ffffff" text="#000000">
<!-- Passes validator.w3.org, please keep it this way;
please, use a max of 72 chars per line -->

<a href="../../index.html">
<img alt="[OpenBSD]" height=30 width=141 src="../../images/smalltitle.gif" border="0">
</a>
<p>
[<a href="nat.html">Previous: Network Address Translation</a>]
[<a href="index.html">Contents</a>]
[<a href="shortcuts.html">Next: Shortcuts For Creating Rulesets</a>]

<h1><font color="#e00000">PF: Redirection (Port Forwarding)</font></h1>

<hr>

<h3>Table of Contents</h3>
<ul>
<li><a href="#intro">Introduction</a>
<li><a href="#filter">Redirection and Packet Filtering</a>
<li><a href="#security">Security Implications</a>
<li><a href="#reflect">Redirection and Reflection</a>
	<ul>
	<li><a href="#splitdns">Split-Horizon DNS</a>
	<li><a href="#sepnet">Moving the Server Into a Separate Local
	Network</a>
	<li><a href="#tcpproxy">TCP Proxying</a>
	<li><a href="#rdrnat">RDR and NAT Combination</a>
	</ul>
</ul>

<hr>

<a name="intro"></a>
<h2>Introduction</h2>
When you have NAT running in your office you have the entire Internet
available to all your machines.  What if you have a machine behind the
NAT gateway that needs to be accessed from outside?  This is where
redirection comes in.  Redirection allows incoming traffic to be sent to
a machine behind the NAT gateway.

<p>
Let's look at an example:
<blockquote>
<tt>
rdr on tl0 proto tcp from any to any port 80 -&gt; 192.168.1.20
</tt>
</blockquote>

<p>
This line redirects TCP port 80 (web server) traffic to a machine inside
the network at 192.168.1.20.  So, even though 192.168.1.20 is behind
your gateway and inside your network, the outside world can access it.

<p>
The <tt>from any to any</tt> part of the above <tt>rdr</tt> line can
be quite useful. If you know what addresses or subnets are supposed
to have access to the web server at port 80, you can restrict them
here:
<blockquote>
<tt>
rdr on tl0 proto tcp from 27.146.49.0/24 to any port 80 -&gt; \<br>
&nbsp;&nbsp;&nbsp;192.168.1.20
</tt>
</blockquote>

<p>
This will redirect only the specified subnet.  Note this implies you can
redirect different incoming hosts to different machines behind the
gateway. This can be quite useful. For example, you could have users
at remote sites access their own desktop computers using the same
port and IP address on the gateway as long as you know the IP address
they will be connecting from:
<blockquote>
<tt>
rdr on tl0 proto tcp from 27.146.49.14 to any port 80 -&gt; \<br>
&nbsp;&nbsp;&nbsp;192.168.1.20<br>
rdr on tl0 proto tcp from 16.114.4.89 to any port 80 -&gt; \<br>
&nbsp;&nbsp;&nbsp;192.168.1.22<br>
rdr on tl0 proto tcp from 24.2.74.178 to any port 80 -&gt; \<br>
&nbsp;&nbsp;&nbsp;192.168.1.23
</tt>
</blockquote>

<p>
A range of ports can also be redirected within the same rule:
<blockquote>
<tt>
rdr on tl0 proto tcp from any to any port 5000:5500 -&gt; \<br>
&nbsp;&nbsp;&nbsp;192.168.1.20<br>
rdr on tl0 proto tcp from any to any port 5000:5500 -&gt; \<br>
&nbsp;&nbsp;&nbsp;192.168.1.20 port 6000<br>
rdr on tl0 proto tcp from any to any port 5000:5500 -&gt; \<br>
&nbsp;&nbsp;&nbsp;192.168.1.20 port 7000:*<br>
</tt>
</blockquote>

<p>
These examples show ports 5000 to 5500 inclusive being redirected to
192.168.1.20.
In rule #1, port 5000 is redirected to 5000, 5001 to 5001, etc.
In rule #2, the entire port range is redirected to port 6000.
And in rule #3, port 5000 is redirected to 7000, 5001 to 7001, etc.

<a name="filter"></a>
<h2>Redirection and Packet Filtering</h2>
<font color="#ff0000">NOTE:</font> Translated packets must still pass
through the filter engine and will be blocked or passed based on the
filter rules that have been defined.

<p>
The <i>only</i> exception to this rule is when the <tt>pass</tt> keyword
is used within the <tt>rdr</tt> rule. In this case, the redirected
packets will pass statefully right through the filtering engine: the
filter rules won't be evaluated against these packets.
This is a handy shortcut to avoid adding <tt>pass</tt> filter rules for
each redirection rule.
Think of it as a normal <tt>rdr</tt> rule (with no <tt>pass</tt> keyword)
associated to a <tt>pass</tt> filter rule with the <tt>keep state</tt>
keyword.
However, if you want to enable more specific filtering options such as
<tt>synproxy</tt>, <tt>modulate state</tt>, etc. you'll still have to
use a dedicate <tt>pass</tt> rule as these options don't fit into
redirection rules.

<p>
Also be aware that since translation occurs <i>before</i> filtering, the
filter engine will see the <i>translated</i> packet as it looks after
it's had its destination IP address and/or destination port changed to
match the redirection address/port specified in the <tt>rdr</tt> rule.
Consider this scenario:

<ul>
<li>192.0.2.1 - host on the Internet
<li>24.65.1.13 - external address of OpenBSD router
<li>192.168.1.5 - internal IP address of web server
</ul>

<p>
Redirection rule:
<blockquote>
<tt>
rdr on tl0 proto tcp from 192.0.2.1 to 24.65.1.13 port 80 \<br>
&nbsp;&nbsp;&nbsp;-&gt; 192.168.1.5 port 8000
</tt>
</blockquote>

<p>
Packet before the <tt>rdr</tt> rule is processed:
<ul>
<li>Source address: 192.0.2.1
<li>Source port: 4028 (arbitrarily chosen by the operating system)
<li>Destination address: 24.65.1.13
<li>Destination port: 80
</ul>

<p>
Packet after the <tt>rdr</tt> rule is processed:
<ul>
<li>Source address: 192.0.2.1
<li>Source port: 4028
<li>Destination address: 192.168.1.5
<li>Destination port: 8000
</ul>

<p>
The filter engine will see the IP packet as it looks after translation
has taken place.

<a name="security"></a>
<h2>Security Implications</h2>
Redirection does have security implications. Punching a hole in the
firewall to allow traffic into the internal, protected network
potentially opens up the internal machine to compromise. If traffic is
forwarded to an internal web server for example, and a vulnerability is
discovered in the web server daemon or in a CGI script run on the web
server, then that machine can be compromised from an intruder on the
Internet. From there, the intruder has a doorway to the internal
network, one that is permitted to pass right through the firewall.

<p>
These risks can be minimized by keeping the externally accessed system
tightly confined on a separate network. This network is often referred to
as a Demilitarized Zone (DMZ) or a Private Service Network (PSN). This
way, if the web server is compromised, the effects can be limited to
the DMZ/PSN network by careful filtering of the traffic permitted to and
from the DMZ/PSN.

<a name="reflect"></a>
<h2>Redirection and Reflection</h2>
Often, redirection rules are used to forward incoming connections from
the Internet to a local server with a private address in the internal
network or LAN, as in:
<blockquote>
<tt>
server = 192.168.1.40<br>
<br>
rdr on $ext_if proto tcp from any to $ext_if port 80 -&gt; $server \<br>
&nbsp;&nbsp;&nbsp;port 80
</tt>
</blockquote>

<p>
But when the redirection rule is tested from a client on the LAN, it
doesn't work. The reason is that redirection rules apply only to
packets that pass through the specified interface (<tt>$ext_if</tt>, 
the external interface, in the example). 
Connecting to the external address of the
firewall from a host on the LAN, however, does not mean the packets will
actually pass through its external interface. The TCP/IP stack on the
firewall compares the destination address of incoming packets with its
own addresses and aliases and detects connections to itself as soon as
they have passed the internal interface. Such packets do not physically
pass through the external interface, and the stack does not simulate
such a passage in any way. Thus, PF never sees these packets on the
external interface, and the redirection rule, specifying the external
interface, does not apply.

<p>
Adding a second redirection rule for the internal interface does not
have the desired effect either. When the local client connects to the
external address of the firewall, the initial packet of the TCP
handshake reaches the firewall through the internal interface. The
redirection rule does apply and the destination address gets replaced
with that of the internal server. The packet gets forwarded back through
the internal interface and reaches the internal server. But the source
address has not been translated, and still contains the local client's
address, so the server sends its replies directly to the client. The
firewall never sees the reply and has no chance to properly reverse the
translation. The client receives a reply from a source it never
expected and drops it. The TCP handshake then fails and no connection
can be established.

<p>
Still, it's often desirable for clients on the LAN to connect to the
same internal server as external clients and to do so transparently.
There are several solutions for this problem: 

<a name="splitdns"></a>
<h3>Split-Horizon DNS</h3>

<p>
It's possible to configure DNS servers to answer queries from local
hosts differently than external queries so that local clients will
receive the internal server's address during name resolution. They will
then connect directly to the local server, and the firewall isn't
involved at all. This reduces local traffic since packets don't have to
be sent through the firewall.

<a name="sepnet"></a>
<h3>Moving the Server Into a Separate Local Network</h3>

<p>
Adding an additional network interface to the firewall and moving the
local server from the client's network into a dedicated network (DMZ)
allows redirecting of connections from local clients in the same way as
the redirection of external connections. Use of separate networks has
several advantages, including improving security by isolating the server
from the remaining local hosts. Should the server (which in our case is
reachable from the Internet) ever become compromised, it can't access
other local hosts directly as all connections have to pass through the
firewall.

<a name="tcpproxy"></a>
<h3>TCP Proxying</h3>

<p>
A generic TCP proxy can be setup on the firewall, either listening on
the port to be forwarded or getting connections on the internal
interface redirected to the port it's listening on. When a local client
connects to the firewall, the proxy accepts the connection, establishes
a second connection to the internal server, and forwards data between
those two connections.

<p>
Simple proxies can be created using 
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=inetd&amp;sektion=8"
>inetd(8)</a> and
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=nc&amp;sektion=1"
>nc(1)</a>. The following <tt>/etc/inetd.conf</tt> entry creates a listening
socket bound to the loopback address (127.0.0.1) and port 5000.
Connections are forwarded to port 80 on server 192.168.1.10.
<blockquote>
<tt>
127.0.0.1:5000 stream tcp nowait nobody /usr/bin/nc nc -w \<br>
&nbsp;&nbsp;&nbsp;20 192.168.1.10 80
</tt>
</blockquote>

<p>
The following redirection rule forwards port 80 on the internal
interface to the proxy:
<blockquote>
<tt>
rdr on $int_if proto tcp from $int_net to $ext_if port 80 -&gt; \<br>
&nbsp;&nbsp;&nbsp;127.0.0.1 port 5000
</tt>
</blockquote>

<a name="rdrnat"></a>
<h3>RDR and NAT Combination</h3>

<p>
With an additional NAT rule on the internal interface, the lacking
source address translation described above can be achieved.
<blockquote>
<tt>
rdr on $int_if proto tcp from $int_net to $ext_if port 80 -&gt; \<br>
&nbsp;&nbsp;&nbsp;$server
<br>
no nat on $int_if proto tcp from $int_if to $int_net<br>
nat on $int_if proto tcp from $int_net to $server port 80 -&gt; \<br>
&nbsp;&nbsp;&nbsp;$int_if
</tt>
</blockquote>

<p>
This will cause the initial packet from the client to be translated
again when it's forwarded back through the internal interface, replacing
the client's source address with the firewall's internal address. The
internal server will reply back to the firewall, which can reverse both
NAT and RDR translations when forwarding to the local client. This
construct is rather complex as it creates two separate states for each
reflected connection. Care must be taken to prevent the NAT rule from
applying to other traffic, for instance connections originating from
external hosts (through other redirections) or the firewall itself. Note
that the <tt>rdr</tt> rule above will cause the TCP/IP stack to see
packets arriving on the internal interface with a destination address
inside the internal network.

<p>
In general, the previously mentioned solutions should be used instead.

<p>
[<a href="nat.html">Previous: Network Address Translation</a>]
[<a href="index.html">Contents</a>]
[<a href="shortcuts.html">Next: Shortcuts For Creating Rulesets</a>]

<p>
<hr>
<a href="index.html"><img height="24" width="24" src="../../images/back.gif" border="0" alt="[back]"></a> 
<a href="mailto:www@openbsd.org">www@openbsd.org</a>
<br>
<small>$OpenBSD: rdr.html,v 1.27 2007/05/06 18:59:54 nick Exp $</small>

</body>
</html> 
